<template>
  <md-page title="数字分身" isRefresh @pullDownRefresh="handlePullDownRefresh">
    <view
      class="container"
      :style="{
        width: '100%',
        height: `calc(100vh - 44px - ${data.statusBarHeight})`,
      }">
      <view class="circle-inter">
        <uni-file-picker
          v-model="data.image"
          :del-icon="false"
          disable-preview
          file-mediatype="image"
          return-type="object"
          :image-styles="data.circleInter"
          :auto-upload="false"
          @select="handleUpload">
          <view class="wrap" v-if="!data.image.url">
            <view class="title">点击上传头像/形象图</view>
            <view class="desc">支持JPG/PNG格式，大小不超过5MB</view>
          </view>
          <image v-else :src="data.image.url" mode="aspectFill" />
        </uni-file-picker>
      </view>

      <!-- 提示文字 -->
      <view class="tip-text">
        <text>上传比赛打卡照片生成专属数字分身，可分享微信、抖音、小红书等。</text>
      </view>

      <!-- 一些花里胡哨的图片 -->
      <view>
        <!-- 生成记录 -->
        <image
          src="https://jogging.qzctdg.com/uploads/ads/20251111/b20e9fd6e8746ce377600d03bed942e9.png"
          class="log-image"
          @click="goRecord" />
        <!-- 叶子 -->
        <image
          src="https://jogging.qzctdg.com/uploads/ads/20251113/ac10a70da03e77d8fa3cb480a95be539.png"
          class="yezi-image" />
        <!-- one兔 -->
        <image
          src="https://jogging.qzctdg.com/uploads/ads/20251113/0d07f1e3268fb9071537456e9915ada5.png"
          class="onetu-image" />
      </view>
      <!-- 底部区域 -->
      <view class="bottom" v-if="!data.isShow">
        <view class="btns">
          <!-- 生成分身 -->
          <image
            src="https://jogging.qzctdg.com/uploads/ads/20251125/4a381a50b047eef1476f849b4df2e1ce.png"
            @click="generate" />
          <!-- 合成形象 -->
          <!-- <image
            src="https://jogging.qzctdg.com/uploads/ads/20251125/2aadbb6be589c5cb570f78ca12020cde.png"
            @click="combine" /> -->
        </view>
        <!-- <view class="title">·生成结果将保留文旅元素与动画质感·</view> -->
        <view class="desc">免费次数：{{ data.info?.restCount || 0 }} / {{ data.info?.freeCount || 0 }}</view>
      </view>
      <!-- 生成漫像操作区域 -->
      <view class="dialog" v-else>
        <view class="wrapper">
          <view class="top">
            <view class="tabs">
              <view
                v-for="(item, index) in data.tabs"
                :key="item"
                class="tab"
                :class="{ active: data.tabActiveIndex == index }"
                @click="() => handleSelectMergeTab(index)">
                {{ item }}
              </view>
            </view>
            <view class="cancel" @click="data.isShow = false">取消</view>
          </view>
          <view class="content">
            <view
              class="card"
              v-for="(item, index) in data.list"
              :key="item.id"
              @click="() => handleSelect(index, item)">
              <view v-if="data.activeIndex == index" class="active"></view>
              <image :src="item.img" class="image" />
            </view>
          </view>
          <!-- 确认按钮 -->
          <view class="confirm">
            <image
              src="https://jogging.qzctdg.com/uploads/ads/20251111/ab83d7d5780a61a6073c6ac05b383962.png"
              @click="handleMergeImage" />
          </view>
        </view>
      </view>
    </view>
  </md-page>
</template>

<script setup lang="ts">
import { reactive } from 'vue';
// 组件
import { onLoad } from '@dcloudio/uni-app';
import {
  cozeComposeImage,
  cozeConfig,
  cozeGetBgPicList,
  cozeGetComposeResult,
  cozeGetResult,
  cozeImageToImage,
  cozePay,
} from '@/api';
import { Toast } from '@/utils/util';
import type { Coze, SignTeam } from '@/api/data';
const data = reactive<any>({
  statusBarHeight: uni.getSystemInfoSync().statusBarHeight + 'px',
  circleInter: {
    width: '440rpx',
    height: '440rpx',
    'border-radius': '50%',
  },
  image: {},
  isShow: false,
  tabs: ['形象', '背景'],
  tabActiveIndex: 0,
  list: [],
  activeIndex: 0,
  execute_id: '',
  order_id: null,
  filePath: '',
  info: {},
  bg_pic_id: '', // 选择的背景图id
});

const goRecord = () => {
  uni.navigateTo({
    url: '/pages/other/ai/digital-twin-history-list',
  });
};

const _getImageObj = (url: string) => {
  const extname = url?.split?.('.')?.slice(-1)[0]; // 后缀
  return {
    url,
    extname,
    name: url?.split?.('/')?.slice(-1)[0] + '.' + extname,
  };
};

// 生成分身
const generate = () => {
  if (data.execute_id) {
    fetchcozeGetResult({ execute_id: data.execute_id });
  } else {
    if (!data.filePath) {
      uni.showModal({
        title: '提示',
        content: '请先上传图片, 再生成分身!',
        showCancel: false,
      });
      return;
    }
    uni.showLoading({
      title: '图片生成中...',
      mask: true,
    });
    cozeImageToImage({ filePath: data.filePath })
      .then(async res => {
        // 需要调用支付
        if (!!res.data?.order_id) {
          payOrder(
            {
              order_id: res.data?.order_id || '',
              order_sn: res.data?.order_sn || '',
            },
            (type, order_id) => {
              const msg = type === 'success' ? '支付成功' : '用户取消支付';
              uni.showModal({
                title: '提示',
                content: msg,
                showCancel: false,
                success: res => {
                  if (res.confirm) {
                    if (type === 'success') {
                      fetchcozeGetResult({ order_id });
                    }
                  }
                },
              });
            }
          );
        } else {
          fetchcozeGetResult({ execute_id: res?.data?.execute_id });
        }
      })
      .finally(() => {
        uni.hideLoading();
      });
  }
};

// 选择合成图类型
const handleSelectMergeTab = (index: number) => {
  data.tabActiveIndex = index;
  fetchcozeGetBgPicList(index + 1);
};

// 选择形象图片或者背景图片
const handleSelect = (index: number, item: Coze.BgPicList.Data) => {
  data.activeIndex = index;
  data.bg_pic_id = item.id;
};

// 合成形象
const combine = () => {
  if (data?.execute_id || data?.order_id) {
    data.isShow = true;
    fetchcozeGetBgPicList(data.tabActiveIndex + 1);
  } else {
    uni.showModal({
      title: '提示',
      content: '请先生成分身, 再合成形象!',
      showCancel: false,
    });
  }
};

// 上传
const handleUpload = (e: any) => {
  console.log('选择文件：', e);
  let { tempFilePaths, tempFiles } = e;
  const size = tempFiles?.[0]?.size;
  const isLt5M = size / 1024 / 1024 < 5;
  if (!isLt5M) {
    data.image = {};
    uni.showModal({
      title: '提示',
      content: '支持JPG/PNG格式，大小不超过5MB',
      showCancel: false,
    });
    return;
  }
  uni.showLoading({
    title: '图片上传中...',
    mask: true,
  });
  data.filePath = tempFilePaths?.[0];
  uni.hideLoading();
};

// 支付
const payOrder = (
  params: Pick<SignTeam.Leader.Data, 'order_id' | 'order_sn'>,
  cb?: (type: 'success' | 'fail', order_id: number) => void
) => {
  uni.showLoading({
    title: '支付中...',
    mask: true,
  });
  // 支付
  cozePay({ ...params })
    .then(res => {
      const payData = res.data;
      if (payData?.timeStamp) {
        uni.showModal({
          title: '提示',
          content: `${res?.msg}`,
          success: m => {
            if (m.confirm) {
              uni.requestPayment({
                provider: 'wxpay',
                orderInfo: {},
                nonceStr: res.data?.nonceStr, // 随机字符串
                package: res.data?.package, // 固定值
                signType: res.data?.signType,
                timeStamp: res.data?.timeStamp, // 时间戳（单位：秒）
                paySign: res.data?.paySign, // 签名，这里用的 MD5/RSA 签名
                success: function (res) {
                  cb?.('success', payData.order_id);
                },
                fail: function (err) {
                  cb?.('fail', 0);
                },
              });
            }
          },
        });
      } else {
        uni.showModal({
          title: '提示',
          content: res?.msg || '支付失败',
          showCancel: false,
        });
      }
    })
    .catch(e => {
      uni.showModal({
        title: '提示',
        content: e?.msg || '支付失败',
        showCancel: false,
      });
    })
    .finally(() => {
      uni.hideLoading();
    });
};

// 合成背景图
const handleMergeImage = async () => {
  data.isShow = false;
  if (data.execute_id) {
    fetchcozeGetComposeResult({ execute_id: data.execute_id });
  } else {
    if (!data.filePath) {
      uni.showModal({
        title: '提示',
        content: '请先上传图片, 再生成分身!',
        showCancel: false,
      });
      return;
    }
    uni.showLoading({
      title: '图片生成中...',
      mask: true,
    });
    cozeComposeImage({
      bg_pic_id: data.bg_pic_id,
      ai_image: data.filePath,
    })
      .then(async res => {
        // 需要调用支付
        if (!!res.data?.order_id) {
          payOrder(
            {
              order_id: res.data?.order_id || '',
              order_sn: res.data?.order_sn || '',
            },
            (type, order_id) => {
              const msg = type === 'success' ? '支付成功' : '用户取消支付';
              uni.showModal({
                title: '提示',
                content: msg,
                showCancel: false,
                success: res => {
                  if (res.confirm) {
                    if (type === 'success') {
                      fetchcozeGetComposeResult({ order_id: order_id });
                    }
                  }
                },
              });
            }
          );
        } else {
          // data.image = _getImageObj(res?.data?.source_image); // 可以不用再拿图片地址做渲染
          fetchcozeGetComposeResult({ execute_id: res?.data?.execute_id });
        }
      })
      .finally(() => {
        uni.hideLoading();
      });
  }
};

/**
 * 接口相关
 */
// 获取caze生成图结果
const fetchcozeGetResult = async (params: Coze.GetResult.Body) => {
  params?.execute_id && (data.execute_id = params.execute_id);
  params?.order_id && (data.order_id = params.order_id);
  try {
    const res = await cozeGetResult({ ...params });
    if (res.data.status === 'success') {
      data.image = _getImageObj(res.data.image_url);
      // 重置
      data.execute_id = '';
      data.order_id = null;
      data.filePath = '';
    }
    if (res.data.status === 'processing') {
      Toast('生分身成中, 请稍等片刻...');
    }
  } catch (error) {}
};

// 获取背景图列表
const fetchcozeGetBgPicList = async (img_type: number) => {
  try {
    const res = await cozeGetBgPicList({ limit: 100, img_type });
    data.list = res.data?.data || [];
  } catch (error) {}
};

// 获取配置
const fetchCozeConfig = async () => {
  try {
    const res = await cozeConfig();
    data.info = res.data;
  } catch (error) {}
};

// 获取caze合成图生成图结果
const fetchcozeGetComposeResult = async (params: Coze.GetResult.Body) => {
  params?.execute_id && (data.execute_id = params.execute_id);
  params?.order_id && (data.order_id = params.order_id);
  try {
    const res = await cozeGetComposeResult({ ...params });
    if (res.data.status === 'success') {
      data.image = _getImageObj(res.data.image_url);
      data.execute_id = '';
      data.order_id = null;
      data.filePath = '';
      data.bg_pic_id = '';
    }
    if (res.data.status === 'processing') {
      Toast('合成分身中, 请稍等片刻...');
    }
  } catch (error) {}
};

onLoad(() => {
  const configInfo = uni.getStorageSync('configInfo') ? JSON.parse(uni.getStorageSync('configInfo')) : '';
  if (configInfo) {
    data.configInfo = configInfo;
  }
  fetchCozeConfig();
});

const handlePullDownRefresh = () => {
  if (data?.execute_id || data?.order_id) {
    if (!data.bg_pic_id) {
      fetchcozeGetResult({ execute_id: data?.execute_id, order_id: data?.order_id });
    } else {
      // 合成图生成结果
      fetchcozeGetComposeResult({ execute_id: data?.execute_id, order_id: data?.order_id });
    }
  }
  fetchCozeConfig();
};
</script>
<style lang="scss" scoped>
.circle-inter {
  position: fixed;
  // top: 510rpx; // md-page 没添加isRefresh前的值
  top: 347rpx;
  left: 8rpx;
  width: 440rpx;
  height: 440rpx;
  border-radius: 50%;
  background: white;
  margin: auto;
  right: 0;
  z-index: 1;
  .wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
  }
  .title {
    font-weight: bold;
    font-size: 34rpx;
    color: #ff7325;
  }
  .desc {
    font-weight: 500;
    font-size: 20rpx;
    color: #808080;
    margin-top: 17rpx;
  }

  ::v-deep .uni-file-picker__container {
    margin: initial;
  }
  ::v-deep .file-picker__box-content {
    border: none;
    margin: 0;
    border-style: initial !important;
  }
  ::v-deep .file-image {
    border-radius: 50%;
  }
  ::v-deep .file-picker__progress {
    display: none;
  }
}

.tip-text {
  position: fixed;
  top: 980rpx;
  left: 50%;
  transform: translateX(-50%);
  width: 680rpx;
  padding: 20rpx 30rpx;
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.85);
  border-radius: 20rpx;
  box-shadow: 0rpx 4rpx 12rpx rgba(0, 0, 0, 0.1);
  z-index: 2;
  text {
    font-size: 24rpx;
    color: #802d00;
    line-height: 1.6;
    text-align: center;
    display: block;
  }
}

.container {
  background: url('https://jogging.qzctdg.com/uploads/ads/20251111/711475484c043ad18588f98cf73e88cc.png') center/cover
    no-repeat;
  display: flex;
  flex-direction: column;
  align-items: center;
  position: relative;
  overflow: hidden;
  .log-image {
    width: 84rpx;
    height: 265rpx;
    position: absolute;
    top: 0;
    right: 0;
  }
  .yezi-image {
    width: 336rpx;
    height: 308rpx;
    position: absolute;
    top: 169rpx;
    right: 0;
    z-index: 2;
  }
  .onetu-image {
    width: 499rpx;
    height: 331rpx;
    position: absolute;
    top: 623rpx;
    right: 33rpx;
    z-index: 2;
  }

  .bottom {
    background: url('https://jogging.qzctdg.com/uploads/ads/20251111/9c2cf06dd4fec4f3bbd0d52301432a9b.png') center/cover
      no-repeat;
    width: 100%;
    height: 406rpx;
    position: absolute;
    bottom: 0;
    left: 0;
    font-weight: bold;
    color: #f0ffcd;
    text-align: center;
    z-index: 3;
    .btns {
      margin-top: 160rpx;
      display: flex;
      justify-content: center;
      image {
        width: 354rpx;
        height: 131rpx;
      }
    }
    .title {
      font-size: 28rpx;
    }
    .desc {
      font-size: 22rpx;
      margin-top: 13rpx;
    }
  }

  .dialog {
    width: 100%;
    height: 600rpx;
    background: url('https://jogging.qzctdg.com/uploads/ads/20251111/dfe237c9ee8b4e44579c9c419bc2992f.png') center/cover
      no-repeat;
    position: absolute;
    bottom: 0;
    left: 0;
    padding: 132rpx 30rpx 0;
    box-sizing: border-box;
    z-index: 3;
    .wrapper {
      .top {
        display: flex;
        align-items: center;
        justify-content: space-between;
        font-weight: bold;
        font-size: 28rpx;
        color: #802d00;
        .tabs {
          display: flex;
          align-items: center;
          gap: 36rpx;
          .tab {
            &.active {
              font-size: 34rpx;
              color: #ff6614;
              padding-bottom: 4rpx;
              border-bottom: 4rpx solid #ff6614;
              box-sizing: border-box;
            }
          }
        }
        .cancel {
          color: #8b3000;
        }
      }
      .content {
        width: 100%;
        margin-top: 50rpx;
        overflow-x: auto;
        min-height: 220rpx;
        display: flex;
        gap: 30rpx;
        .card {
          width: 180rpx;
          height: 220rpx;
          position: relative;
          .active {
            position: absolute;
            left: 0;
            top: 0;
            z-index: 1;
            width: 180rpx;
            height: 220rpx;
            box-shadow: 0rpx 0rpx 10rpx 0rpx #b33e00;
            border-radius: 30rpx;
            border: 4px solid #ff6614;
            box-sizing: border-box;
          }
          image {
            border-radius: 30rpx;
            flex: 0 0 auto;
            width: 180rpx;
            height: 220rpx;
            object-fit: cover;
          }
        }
      }
      .confirm {
        margin-top: 20rpx;
        display: flex;
        justify-content: center;
        image {
          width: 450rpx;
          height: 131rpx;
        }
      }
    }
  }
}
</style>
an
